## Описание

Цель работы: научиться оборачивать интерфейсы и добиться такой универсальности
обертки, чтобы не зависеть от того, какой именно интерфейс она оборачивает.
В качестве примера обернута одна функция `setTimeout`, а в качестве задания мы
обернем целый интерфейс работы с файловой системой - библиотеку `fs`.

## Файлы

* `framework.js` - кусочек фреймворка, демонстрирующий обертку (wrapper)
* `application.js` - часть приложения для демонстрации обертки

## Запуск

Из командной строки пишем `node application`, а потом `node framework`,
сравниваем вывод, смотрим код `framework.js` и `application.js`, понимаем
как работает обертка вокруг `setTimeout`

## Задание

1. Нужно изучить, как обернут таймер.
По аналогии с оберткой таймера нужно сделать обертку вокруг модуля fs.
Все его функции нужно пройти в цикле `for (const key in fs) { ... }` и заменить
на свою функцию. При помощи замыкания эта функция должна стать универсальной
прослойкой для всех функций библиотеки fs. Смысл обертки - логировать все
вызовы к файловой системе в файл, с указанием времени, имени функции, ее
аргументов, а если функция имеет еще и callback, то нужно перехватывать и его,
тоже логируя в файл момент, вызова callback. Это задание можно разбить на
несколько шагов.
2. Удаляем из `application.js` вызов таймера и оставляем там только код:

```JavaScript
const fileName = './README.md';
console.log('Application going to read ' + fileName);
fs.readFile(fileName, (err, src) => {
  console.log('File ' + fileName + ' size ' + src.length);
});
```

Это пример работы с файлом. И мы будем изменять поведение этого кода.
Убираем из `framework.js` обертку таймера и пробрасываем `fs` в приложение.
Теперь запускаем `node framework` и убеждаемся, что файл считывается и
выводится его длина.
3. Теперь пишем функцию `cloneInterface(interfaceName)` для копирования всех
ключей из библиотеки `fs` в новый интерфейс и передаем в песочницу не исходный
`fs`, а склонированный. Пример функции клонирования:

```JavaScript
const cloneInterface = (anInterface) => {
  const clone = {};
  for (const key in anInterface) {
    clone[key] = anInterface[key];
  }
  return clone;
};
```

4. Пишем функцию `wrapFunction(fnName, fn)` которая оборачивает функцию `fn` и
возвращает функцию-замыкание от `wrapper`. Замыкание, это ссылка на копию
функции `wrapper`, которая замкнута на контекст `wrapFunction`. Таким образом
мы применяем функциональное наследование и порождаем такой вариант `wrapper`,
который видит параметры `fnName` и 'fn' от `wrapFunction`. Мы полностью
передаем все аргументы в функцию fn:

```JavaScript
const wrapFunction = (fnName, fn) => (...args) => {
  console.log('Call: ' + fnName);
  console.dir(args);
  return fn(...args);
}
```

5. Определяем, есть ли среди аргументов `callback`, он всегда последний в
массиве аргументов и его тип `function`. Если `callback` есть, то вместо него
передаем свою функцию, которая логирует все аргументы и вызывает настоящий
`callback`.
6. Теперь можно из `application.js` использовать другие функции `fs` и
убедиться, что все они обернуты.
7. Добавляем таймеры в `application.js` и на таймерах работаем с файлами, а из
`framework.js` собираем статистику работы с файлами и выводим ее каждые 30
секунд. Например, можно собирать несколько из этих параметров
  - количество обращений к функциям,
  - количество колбэков,
  - среднюю скорость завершения функций,
  - среднюю скорость возвращения колбеков,
  - общий объем прочитанных с диска данных,
  - общий объем записанных данных,
  - среднюю скорость чтения и записи,
  и т.д.

Сохраните наработки этой лабораторной работы, они понадобятся для выполнения
следующих работ, в частности, работы по передаче вызовов в другой процесс и
другой сервер. Это позволит распределить исполнение приложения.

## Дополнительные задания

8. Попробуйте подставить в качестве файловой системы свою структуру из памяти,
и прочитать из нее при помощи `fs.readFile`, потом записать файл, создать и
удалить. Пример структуры:

```JavaScript
const virtualFs = {
  folder: {
    subfolder: {
      file1: 'File content',
      file2: 'Another file content'
    },
  },
  notes: {
    myToDos: 'Refactor projects, Prepare tests',
    meetings: 'Meet thoughts at 10:00 walking along garden'
  }
};
```

9. Реализуйте кеширование файловых операций в памяти.
10. Напишите аналогичный пример на другом языке программирования.
